from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.api import memcache
from google.appengine.runtime import apiproxy_errors

import random
import itertools
import time
import logging

from models.gamedata import *
from models.gameflow import *
from models import gamelog

import logging

API_VERSION="0.51"

class ApiException(GameException):
    def __init__(self, code=None, message=None):
            self.code = code or - 33000
            self.message = message or "Api Error"
            
class GameManagerException(ApiException):
    pass

class ApiBase(object):
    def __init__(self): 
        #last_seen=memcache.get("last_seen_%s" % users.get_current_user().email())
        memcache.set("last_seen_%s" % users.get_current_user().email(), time.time())
        
        
    def _get_gamestate_by_id(self, game_id, validate_user=False):
        try:
            while True:
                timeout_s = 0.1
                try:
                    gamestate = GameState.get_by_id(game_id)
                    break;
                except db.Timeout:
                    logging.info("Got timeout fetching game state, trying again in %2f seconds" % timeout_s)
                    time.sleep(timeout_s)
                    timeout_s *= 2
        except apiproxy_errors.DeadlineExceededError:
            raise ApiException, "Timeout accessing the database"

        if not isinstance(gamestate, GameState):
            raise ApiException, (-33003, "%s is not a valid game_id" % game_id)
        if validate_user:
            user = users.get_current_user()
            if gamestate.players.filter("user =", user).count() == 0:
                raise ApiException, (-33004, "You must be playing in game %s" % game_id)
        return gamestate
    
    def _get_player(self, game, player_position=None):
        gamestate = game if isinstance(game, GameState) else self._get_gamestate_by_id(game)
        try:
            while True:
                timeout_s = 0.1
                try:
                    player = gamestate.players.filter("position =", player_position).get() if player_position else gamestate.players.filter("user =", users.get_current_user()).get()
                    break;
                except db.Timeout:
                    logging.info("Got timeout fetching player info, trying again in %2f seconds" % timeout_s)
                    time.sleep(timeout_s)
                    timeout_s *= 2
        except apiproxy_errors.DeadlineExceededError:
            raise ApiException, "Timeout accessing the database"

        if not player:
            raise ApiException, "Could not get player details for game %d, %s" % (gamestate.key().id(), "player_position %d" % player_position if player_position else "current player")
        return player    
    
    def game_info(self, game):
        """Returns detailed info for the specified [game_id]"""
        gamestate = game if isinstance(game, GameState) else self._get_gamestate_by_id(game)
        try:
            while True:
                timeout_s = 0.1
                try:
                    owner = gamestate.players.filter("is_owner =", True).get()
                    current_player = gamestate.current_player
                    start_player = gamestate.start_player
                    logged_in_player = gamestate.players.filter("user =", users.get_current_user()).get()
                    players = [self._player_data(p) for p in sorted(list(gamestate.players), lambda a, b: cmp(a.position, b.position))]
                    break;
                except db.Timeout:
                    logging.info("Got timeout fetching game state, trying again in %2f seconds" % timeout_s)
                    time.sleep(timeout_s)
                    timeout_s *= 2
        except apiproxy_errors.DeadlineExceededError:
            raise ApiException, "Timeout accessing the database"
                
        return {"game_id":gamestate.key().id(), "name":gamestate.name, "round":gamestate.round,
                "phase":gamestate.phase, "current_player":current_player.position if current_player else None,
                "logged_in_player": logged_in_player.position if logged_in_player else None,
                "start_player":start_player.position if start_player else None,
                "owner":owner.position if owner else None, "players":players, "timestamp": gamelog.timestamp()}

    def player_info(self, game, position):
        return self._player_data(self._get_player(game, position))

    def player_ping(self, email):
        if isinstance(email, str):
            return self._player_ping(email)
        elif isinstance(email, list):
            return [{"email":e, "ping":self._player_ping(e)} for e in email]
        else:
            raise ApiException, (-33008, "Invalid argument for player_ping (%r)",email)
        
    def _player_data(self, player):            
        return ApiData(email=player.user and player.user.email(), nickname=player.user and player.user.nickname(), ping=self._player_ping(player.user.email())
                       ).copy_from(player, "name", "position", "colour","has_passed","is_ready")

    def _player_ping(self, email):
        last_seen=memcache.get("last_seen_%s" % email)
        return int(time.time()-last_seen) if last_seen else -1

    def _get(self, query):
        try:
            while True:
                timeout_s = 0.1
                try:
                    result=query.get();
                    break;
                except db.Timeout:
                    logging.info("Got timeout doing api get, trying again in %2f seconds" % timeout_s)
                    time.sleep(timeout_s)
                    timeout_s *= 2
        except apiproxy_errors.DeadlineExceededError:
            raise ApiException, "Timeout accessing the database"
          
        return result

    def _fetch(self, query, num_results=1000):
        try:
            while True:
                timeout_s = 0.1
                try:
                    result=query.fetch(num_results);
                    break;
                except db.Timeout:
                    logging.info("Got timeout doing api fetch, trying again in %2f seconds" % timeout_s)
                    time.sleep(timeout_s)
                    timeout_s *= 2
        except apiproxy_errors.DeadlineExceededError:
            raise ApiException, "Timeout accessing the database"
          
        return result

    def _put(self, model):
        try:
            while True:
                timeout_s = 0.1
                try:
                    db.put(model);
                    break;
                except db.Timeout:
                    logging.info("Got timeout doing api put, trying again in %2f seconds" % timeout_s)
                    time.sleep(timeout_s)
                    timeout_s *= 2
        except apiproxy_errors.DeadlineExceededError:
            raise ApiException, "Timeout accessing the database"

    def _db_get(self, keys):
        try:
            while True:
                timeout_s = 0.1
                try:
                    result=db.get(keys);
                    break;
                except db.Timeout:
                    logging.info("Got timeout doing api db_get, trying again in %2f seconds" % timeout_s)
                    time.sleep(timeout_s)
                    timeout_s *= 2
        except apiproxy_errors.DeadlineExceededError:
            raise ApiException, "Timeout accessing the database"
          
        return result
      
    def _log(self, log_class, game, *args, **kwds):
        """
        Method to log game events. Should be passed a valid class derived from GameLogEntryBase (see gamelog.py), 
        with any proprty values passed as keyword args
        Automatically adds the game, timestamp, type and command properties.
        """
        kwds.update({"parent":game,"game":game, "timestamp":long(timestamp()), "type":log_class.__name__, "command":type(self).__name__})     
        log_entry=log_class(**kwds)
        try:
            while True:
                timeout_s = 0.1
                try:
                    log_entry.put();
                    break;
                except db.Timeout:
                    logging.info("Got timeout fetching game state, trying again in %2f seconds" % timeout_s)
                    time.sleep(timeout_s)
                    timeout_s *= 2
        except apiproxy_errors.DeadlineExceededError:
            raise ApiException, "Timeout accessing the database"
                       
class ApiData():
    """
    Object used for creating a Dynamic class to hold return values for API calls
    Note that for XMLRPC calls, the returned data is still marshalled into a dictionary
    """
    def __init__(self, *args, **kwds):
        for p in kwds.keys():
            setattr(self, p, kwds[p])
    
    def copy_from(self, obj, *args):
        for p in args:
            setattr(self, p, getattr(obj, p, None))
        return self

    def copy_from_dict(self, dict, *args):
        for k in args:
            if k in dict:
                setattr(self, k, dict[k])
        return self
    
class PlayInfo(ApiBase): 
    def __init__(self):
        ApiBase.__init__(self)
       
    def log_info(self, game_id, last_seen_timestamp, level=None):
        gamestate = self._get_gamestate_by_id(game_id)
        query = gamestate.gamelogentries.filter("timestamp >", long(last_seen_timestamp))
        if (isinstance(level, list)):
            query = query.filter("level in", level)
        elif level:
            query = query.filter("level =", level)
        
        try:
            while True:
                timeout_s = 0.1
                try:
                    new_log_entries = list(query)
                    break;
                except db.Timeout:
                    logging.info("Got timeout fetching log entries, trying again in %2f seconds" % timeout_s)
                    time.sleep(timeout_s)
                    timeout_s *= 2
        except apiproxy_errors.DeadlineExceededError:
            raise ApiException, "Timeout accessing the database"
        
        if new_log_entries:
            return [l.to_dictionary() for l in new_log_entries]
        else:
            return None

    def boardstate_info(self, game_id):
        boardstate_info = {}
        gamestate = self._get_gamestate_by_id(game_id)
        boardstate = gamestate.boardstate
        boardstate_info["regions"] = [self._region_data(r) for r in list(boardstate.regions)]
        boardstate_info["building_stock"] = self._building_stock_data(gamestate.buildingstock)
        boardstate_info["connections"] = self._connection_data(list(boardstate.connections))
        boardstate_info["slavery_abolished"]=boardstate.slavery_abolished
        boardstate_info["discarded_governor_cards"]=boardstate.discarded_governor_cards
        
        return boardstate_info
    
    def region_info(self, game_id, region_number):
        gamestate = self._get_gamestate_by_id(game_id, True)
        r = gamestate.boardstate.regions.filter("number =", region_number).get()
        if not r:
            raise ApiException, "Region %d does not exists for game %d" % (region_number, game_id)
        return self._region_data(r)

    def connection_info(self, game_id):
        gamestate = self._get_gamestate_by_id(game_id)
        c = list(gamestate.boardstate.connections)
        if not c:
            raise ApiException, "Cannot get connection data for game %d" % game_id
        return self._connection_data(c)

    def city_info(self, game_id):
        gamestate = self._get_gamestate_by_id(game_id)
        c = City.all().filter("region in", list(gamestate.boardstate.regions))
        if not c:
            raise ApiException, "Cannot get city data for game %d" % game_id
        return self._city_data(c)
        
    def building_stock_info(self, game_id):
        gamestate = self._get_gamestate_by_id(game_id)
        b = gamestate.buildingstock
        if not b:
            raise ApiException, "Cannot get building stock data for game %d" % game_id
        return self._building_stock_data(b)

    def player_board_info(self, game_id, player_position=None):
        pb = self._get_player(game_id, player_position).playerboard
        if not pb:
            raise ApiException, "Cannot get player board data for game %d, player %d" % (game_id, player_position)
        return self._player_board_data(pb)
      
    def player_info(self, game_id, player_position=None):
        player = self._get_player(game_id, player_position)
        return self._player_data(player)
    
    def game_score_info(self, game_id):
        gamestate = self._get_gamestate_by_id(game_id)
        if gamestate.phase!="gameover":
            raise ApiException, "Cannot get game score for  game %d because it has not yet finished" % game_id
        return self._game_score_data(gamestate)
            
    
    def _region_data(self, region):
        region_data = {"number":region.number, "name":region.name}
        if region.shipping_track:
            region_data["shipping_track"] = self._shipping_track_data(region.shipping_track)
        if region.card_pile:
            region_data["card_pile"] = self._card_pile_data(region.card_pile)
        if region.slavery_card_pile:
            region_data["slavery_card_pile"] = self._card_pile_data(region.slavery_card_pile)
        region_data["cities"] = self._city_data(list(region.cities.order("number")))
        
        return region_data
    
    def _shipping_track_data(self, shipping_track):
        track_data = []
        occupiers = Player.get(shipping_track.occupiers) if shipping_track.occupiers else []
        for o in occupiers:
            track_data.append(str(o.position))
        for i in shipping_track.items:
            track_data.append(i)
        return track_data

    def _card_pile_data(self, card_pile):
        return [ApiData(card_id=i, bonus=CardDeck.card_value(i)) for i in card_pile.cards]
    
    def _building_stock_data(self, building_stock):
        return [ApiData(count=building_stock.buildings[i]).
                copy_from_dict(BuildingTiles.buildings[i], "name", "level", "action", "bonus")
                    for i in range(len(building_stock.buildings))]
        
    def _city_data(self, cities):
        return[{"name":c.name, "number":c.number, "occupier":c.occupier.position if c.occupier else None, "trade_token":c.trade_token if c.trade_token else None, "point_value":c.point_value} for c in cities]         
    
    def _connection_data(self, connections):
#        return[{"occupier":c.occupier, "trade_token":c.trade_token,"a_end":c.a_end.number, "b_end":c.b_end.number} for c in connections] 
        return[ApiData(a_end=c.a_end.number, b_end=c.b_end.number, occupier=c.occupier.position if c.occupier else None, trade_token=c.trade_token) for c in connections] 

    def _player_board_data(self, player_board):
        return ApiData(buildings=[{"name":b[0], "bonus":BuildingTiles.bonus(b[0]),
                                   "action":BuildingTiles.action(b[0]), "occupied":b[1]
                                  } for b in map(None, player_board.buildings, player_board.buildings_occupied)],
                       cards=[{"card_id":c, "bonus":CardDeck.card_value(c)} for c in player_board.cards]
                       ).copy_from(player_board, "score_tokens", "harbour_population", "harbour_tokens",
                                   "industry", "culture", "finance", "politics", "glory", "build_level", "growth_level", "salary_level",
                                   "card_limit", "discarded_slavery_cards") 
    def _game_score_data(self, gamestate):
        game_score_data=ApiData(player_score_data=[]);
        for ps in list(gamestate.player_scores.order("finish_position")):
            data=self._player_data(ps.player).copy_from(ps,"finish_position","industry","culture",
                        "finance","politics","occupied_cities","controlled_connections","card_glory",
                        "free_govenor","building_glory","extra_population","slavery","total");
            game_score_data.player_score_data.append(data)
            
        return game_score_data 
    
class PlayManager(ApiBase):
    def __init__(self):
        ApiBase.__init__(self)

    def _gameFlowCommand(self, game_id, *args):
        gamestate = self._get_gamestate_by_id(game_id, True)
        gameflow = EndeavorGameFlow(gamestate)
        gameflow.process(*args)
        try:
            while True:
                timeout_s = 0.1
                try:
                    gameflow.put()
                    return self.game_info(gamestate)
                except db.Timeout:
                    logging.info("Got timeout writing gameflow, trying again in %2f seconds" % timeout_s)
                    time.sleep(timeout_s)
                    timeout_s *= 2
        except apiproxy_errors.DeadlineExceededError:
            raise ApiException, "Timeout accessing the database"
        
    def build(self, game_id, building):
        """select building to build during the build phase"""
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id, BuildCommand, player, building)
        
    def pay(self, game_id, buildings):
        """pay salary during the salary phase"""
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,PayCommand, player, buildings)


    def activate_building_ship(self, game_id, building, region_number):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id, ActivateBuildingShipCommand, player, building, region_number)

    def activate_building_draw(self, game_id, building, region_number, slavery_pile):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id, ActivateBuildingDrawCommand, player, building, region_number, slavery_pile)

    def activate_building_draw_and_draw(self, game_id, building, region_number, slavery_pile_1, slavery_pile_2):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,ActivateBuildingDrawAndDrawCommand, player, building, region_number, slavery_pile_1, slavery_pile_2)

    def activate_building_occupy(self, game_id, building, city_number):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,ActivateBuildingOccupyCommand, player, building, city_number)

    def activate_building_attack(self, game_id, building, city_number):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,ActivateBuildingAttackCommand, player, building, city_number)

    def activate_building_pay(self, game_id, building, target_building):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,ActivateBuildingPayCommand, player, building, target_building)
  
    def activate_building_occupy_and_ship(self, game_id, building, city_number, region_number):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,ActivateBuildingOccupyAndShipCommand, player, building, city_number, region_number)

    def activate_building_ship_and_ship(self, game_id, building, region_number):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,ActivateBuildingShipAndShipCommand, player, building, region_number)

    def use_draw_token(self, game_id, token, region_number, slavery_pile):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,UseDrawTokenCommand, player, token, region_number, slavery_pile)

    def use_ship_token(self, game_id, token, region_number):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,UseShipTokenCommand, player, token, region_number)

    def use_occupy_token(self, game_id, token, city_number):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,UseOccupyTokenCommand, player, token, city_number)

    def use_attack_token(self, game_id, token, city_number):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,UseAttackTokenCommand, player, token, city_number)

    def use_pay_token(self, game_id, token, building):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,UsePayTokenCommand, player, token, building)
    
    def pass_action(self, game_id):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,PassCommand, player)
        
    def discard(self, game_id, card_list):
        player = self._get_player(game_id)
        return self._gameFlowCommand(game_id,DiscardCommand, player, card_list)

    def say(self, game_id, what):
        gamestate = self._get_gamestate_by_id(game_id)
        player = self._get_player(game_id)
        self._log(GameFlowLogEntry, game=gamestate, message="Player %d (%s) said: %s" % (player.position,  player.name, what))
      
class GameManager(ApiBase):
    def __init__(self):
        ApiBase.__init__(self)
       
    def list_games(self, new_only=False, user_only=False, show_completed=False):
        """returns a list of all games"""
        user=users.get_current_user()
        if user_only:
            players=self._fetch(Player.all().filter('user =', user))           
            gamelist=self._db_get([p.game.key() for p in list(players)])
            if new_only:
                gamelist = [g for g in gamelist if g.round == 0]
            if not show_completed:
                gamelist = [g for g in gamelist if g.phase != "gameover"]
        else:
            gamelist = GameState.all()
            if new_only:
                gamelist.filter("round =", 0)
            if not show_completed:
                gamelist.filter("phase !=", "gameover")
                
        games=[]
        for g in list(gamelist):
            num_players=g.players.count()
            is_user_game=user in [p.user for p in g.players]
            user_can_join=not is_user_game and g.round==0 and g.players.count()<5
            user_can_start=is_user_game and g.round==0 and user in [p.user for p in g.players if p.is_owner] and num_players>1
            requires_password=g.password != None
            games.append({"name":g.name, "id": g.key().id(), "num_players":num_players, "round":g.round, 
                          "is_user_game":is_user_game, "user_can_join":user_can_join, "user_can_start":user_can_start, "requires_password":requires_password })
        return games
    
    def create_game(self, name, password=None):
        """creates a new game with the name 'name' and optional password 'password'"""
        gamestate = GameState(name=name, password=password or None)
        game_id = gamestate.put().id()    
        user = users.get_current_user()
        player = Player(parent=gamestate,game=gamestate, position=1, name=user.nickname(), user=user, colour='red', is_owner=True).put()
        PlayerBoard(parent=player,player=player).put()
        return game_id
 
    def delete_game(self, game_id):
        """deletes the game with the provided 'game_id'"""
        user = users.get_current_user()
        gamestate = self._get_gamestate_by_id(game_id)
        gameowner = gamestate.players.filter("is_owner =", True).get()
        if gameowner and user != gameowner.user:
            raise GameManagerException, "Only the game owner can delete a game"
        if gamestate.round != 0 and gamestate.players.count() > 1:
            raise GameManagerException, (-33002, "You cannot delete a game with active players once it has started")
        gamestate.full_delete()
        
    def start_game(self, game_id):
        """starts the game with the provided 'game_id'"""    
        user = users.get_current_user()
        gamestate = self._get_gamestate_by_id(game_id)
        gameowner = gamestate.players.filter("is_owner =", True).get()
        if gamestate.round != 0:
            raise GameManagerException, (-33007, "That game has already started")        
        if user==None or user != gameowner.user:
            raise GameManagerException, (-33006, "Only the game owner can start a game")
        if gamestate.players.count() < 2:
            raise GameManagerException, (-33005, "A game must have at least 2 players before you can start it")
        if gamestate.players.filter("is_ready =", False).get()!=None:
            raise GameManagerException, (-33009, "All players in a game must be ready before you can start it")
            
        gamestate.start_game()
        gameflow=EndeavorGameFlow(gamestate)
        gameflow.process()
        gameflow.put()
        
        logging.info("Started Game: %d" % game_id)
        self._log(GameFlowLogEntry, gamestate, message="Starting Game: %d" % game_id)
        self._log(GameStartLogEntry, gamestate);
                    
        return self.game_info(gamestate)
        
    def join_game(self, game_id, password=None):
        """Adds the current logged in user to the game 'game_id'"""
        user = users.get_current_user()
        gamestate = self._get_gamestate_by_id(game_id)
        db.run_in_transaction(self._join_game_transaction, gamestate.key(), user, password)
        p=self._player_data(self._get(Player.all().ancestor(gamestate).filter("user =",user)))
        self._log(GameFlowLogEntry, gamestate, message="%s joined the game" % p.name)
        self._log(PlayerJoinLogEntry, gamestate, position=p.position,email=p.email,nickname=p.nickname,name=p.name,colour=p.colour);
        return self.game_info(game_id)
            
    def _join_game_transaction(self, gamestate_key, user, password):
        gamestate=db.get(gamestate_key)
        if Player.all().ancestor(gamestate).filter("user =", user).get():
            raise GameManagerException, (-33010, "You have already joined that game")
        if gamestate.round != 0:
            raise GameManagerException, (-33017, "That game has already started")
        if gamestate.password and gamestate.password!=password:
            raise GameManagerException (-33016, "Incorrect password supplied")

        all_positions = (1, 2, 3, 4, 5)
        used_positions = [p.position for p in Player.all().ancestor(gamestate).fetch(5)]
        if len(used_positions) == len(all_positions):
            raise GameManagerException, (-33018, "There are no free positions in that game")
        position = [p for p in all_positions if p not in used_positions][0]        
        all_colours = ["red", "white", "green", "purple", "black"]
        used_colours = [p.colour for p in Player.all().ancestor(gamestate).fetch(5)]
        colour = random.choice([c for c in all_colours if c not in used_colours])
        player = Player(parent=gamestate,game=gamestate, position=position, name=user.nickname(), user=user, colour=colour).put()
        PlayerBoard(parent=player, player=player).put()
                
    def leave_game(self, game_id):    
        gamestate = self._get_gamestate_by_id(game_id)
        player = self._get_player(gamestate)
        new_owner=None
        if not player:
            raise GameManagerException, (-33020, "You have not joined that game")
        players=self._fetch(gamestate.players.filter("user !=", users.get_current_user()))
        if len(players) == 0:
            raise GameManagerException, (-33021, "You are the last player. You must delete that game in order to leave it")
        if player.is_owner:
            new_owner = random.choice(players)
            new_owner.is_owner = True
            self._put(new_owner)
        if gamestate.round == 0:
            player.full_delete()
        else:
            player.user = None
            self._put(player)

        self._log(GameFlowLogEntry, gamestate, message="Player %d (%s) left the game" % (player.position, player.name) )
        if (new_owner):
            self._log(GameFlowLogEntry, gamestate, message="Game owner is now player %d (%s)" % (new_owner.position, new_owner.name) )
            
        self._log(PlayerLeaveLogEntry, gamestate, position=player.position, new_owner_position=new_owner.position if new_owner else None)
        
    def player_ready(self, game_id, is_ready):
        game = self._get_gamestate_by_id(game_id)
        player = self._get_player(game)
        if not player:
            raise GameManagerException, (-33020, "You have not joined that game")
        player.is_ready=is_ready
        self._put(player)

        self._log(GameFlowLogEntry, game, message="Player %d (%s) %s ready" % (player.position, player.name, "is" if is_ready else "is not"))
        self._log(PlayerReadyLogEntry, game, position=player.position, is_ready=is_ready)
        
        return self.game_info(game)
    
    def change_player_colour(self, game_id, colour):
        game = self._get_gamestate_by_id(game_id)
        db.run_in_transaction(self._change_player_colour_transaction, game, colour)
        player=self._get_player(game)
        self._log(GameFlowLogEntry, game, message="Player %d (%s) changed colour to %s" % (player.position, player.name, player.colour))
        self._log(PlayerColourLogEntry, game, position=player.position, colour=player.colour)
        return self.game_info(game)
    
    def _change_player_colour_transaction(self, game, colour):    
        player=Player.all().ancestor(game).filter("user =", users.get_current_user()).get()
        if not player:
            raise GameManagerException, (-33020, "You have not joined that game")
        
        all_colours = ["red", "white", "green", "purple", "black"]
        used_colours = [p.colour for p in Player.all().ancestor(game).fetch(5)]
        if colour not in all_colours:
            raise GameManagerException, (-33035, "%s is not a valid colour" % colour)
        if colour in used_colours:
            raise GameManagerException, (-33035, "The colour %s is is already in use" % colour)
        player.colour=colour
        self._put(player)
    
    def whoami(self):
        """Return the currently logged in users nickname and email"""
        user = users.get_current_user()
        return {"email":user.email(), "nickname":user.nickname()}
    
    def get_current_timestamp(self):
        return gamelog.timestamp()
        
